package ca.etsmtl.log320;

import java.util.Hashtable;

public class Evaluation {

	private static final int DANGER = 0;
	private static final int PROTECT = 1;
	private static final int HOMEVALUE = 2;
	private static final int CORNERPROTECT = 3;
	private static final int HLINE = 4;
	private static final int WIN = 5;

	private static Hashtable<BoardHash, Integer> previousMap = new Hashtable<BoardHash, Integer>();

	public static void clearPrevious() {
		previousMap.clear();
	}
	
	public static int getBoardValue (long[] array, int playerColor){
		if(playerColor == Consts.CASE_BLANC) {
			return Evaluation2.getBoardValue(array, playerColor);
		} else {
			

			Integer previousValue = previousMap.get(MiniMax.getHashCode(array));		
			if(previousValue != null) {
				return previousValue.intValue();
			}
			
			int value = 0;
			int[] detailedValue = getBoardValueDetailed(array, playerColor);
			value += (detailedValue[Consts.PIECEVALUE_PLAYER] - detailedValue[Consts.PIECEVALUE_OPPONENT]) * Consts.PIECEVALUE;
			value += (detailedValue[Consts.HOMEVALUE_PLAYER] - detailedValue[Consts.HOMEVALUE_OPPONENT]) * Consts.HOMEVALUE;
			value += (detailedValue[Consts.CORNERPROTECT_PLAYER] - detailedValue[Consts.CORNERPROTECT_OPPONENT]) * Consts.CORNERPROTECT;
			value += (detailedValue[Consts.DANGER_PLAYER] - detailedValue[Consts.DANGER_OPPONENT]) * Consts.DANGER;
			value += (detailedValue[Consts.PROTECT_PLAYER] - detailedValue[Consts.PROTECT_OPPONENT]) * Consts.PROTECT;
			value += (detailedValue[Consts.FREE_COLUMN_PLAYER] - detailedValue[Consts.FREE_COLUMN_OPPONENT]) * Consts.FREE_COLUMN;
			value += (detailedValue[Consts.HLINE_PLAYER] - detailedValue[Consts.HLINE_OPPONENT]) * Consts.HLINE;
			value += (detailedValue[Consts.WIN2_PLAYER] - detailedValue[Consts.WIN2_OPPONENT]) * Consts.WIN2;
			
//			if (Consts.ALL_EVALUATION_DEBUG){
//				printDetails(detailedValue, false, value);
//			}

			previousMap.put(MiniMax.getHashCode(array), value);
			return value;
		}
	}
	
	public static int[] getBoardValueDetailed(long[] array, int playerColor) {

		int[] returnArray = new int[Consts.BOARD_ARRAY_LENGTH];

		boolean playerHasFreeColumn = false;
		boolean opponentHasFreeColumn = false;

		for (int x = 0; x < 8; x++) {
			playerHasFreeColumn = true;
			opponentHasFreeColumn = true;

			for (int y = 0; y < 8; y++) {
				int pieceColor = Board.get(array, x, y);

				if (pieceColor == Consts.CASE_VIDE){
					continue;
				}

				int[] pieceArray = getPieceValue(array, x, y, pieceColor, playerColor);

				if (pieceColor == playerColor) {
					returnArray[Consts.PIECEVALUE_PLAYER]++;
					returnArray[Consts.DANGER_PLAYER] += pieceArray[DANGER];
					returnArray[Consts.PROTECT_PLAYER] += pieceArray[PROTECT];
					returnArray[Consts.HOMEVALUE_PLAYER] += pieceArray[HOMEVALUE];
					returnArray[Consts.CORNERPROTECT_PLAYER] += pieceArray[CORNERPROTECT];
					returnArray[Consts.HLINE_PLAYER] += pieceArray[HLINE];
					returnArray[Consts.WIN2_PLAYER] += pieceArray[WIN];
					playerHasFreeColumn = false;
				}

				else {
					returnArray[Consts.PIECEVALUE_OPPONENT]++;
					returnArray[Consts.DANGER_OPPONENT] += pieceArray[DANGER];
					returnArray[Consts.PROTECT_OPPONENT] += pieceArray[PROTECT];
					returnArray[Consts.HOMEVALUE_OPPONENT] += pieceArray[HOMEVALUE];
					returnArray[Consts.CORNERPROTECT_OPPONENT] += pieceArray[CORNERPROTECT];
					returnArray[Consts.HLINE_OPPONENT] += pieceArray[HLINE];
					returnArray[Consts.WIN2_OPPONENT] += pieceArray[WIN];
					opponentHasFreeColumn = false;
				}
			}
			if (playerHasFreeColumn) {
				returnArray[Consts.FREE_COLUMN_PLAYER]++;

			}
			if (opponentHasFreeColumn) {
				returnArray[Consts.FREE_COLUMN_OPPONENT]++;
			}
		}

		return returnArray;
	}

	private static int[] getPieceValue(long[] array, int x, int y, int pieceColor, int playerColor) {

		int[] returnArray = new int[6];
		
		boolean leftBound = x == 0;
		boolean rightBound = x == 7;

		if (pieceColor == Consts.CASE_NOIR) {
			if (y == 7)
				returnArray[WIN]++; // NOIR GAGNE
			else {
				if (y == 0)
					returnArray[HOMEVALUE]++;
				if (leftBound) {
					if (y == 1){
						returnArray[CORNERPROTECT]++;
					}
				}
				else{
					int[] attackPostionArray = getAttackPositionValue(Board.get(array, x - 1, y + 1), pieceColor, playerColor);
					returnArray[DANGER] += attackPostionArray[DANGER];
					returnArray[PROTECT] += attackPostionArray[PROTECT];
				}
				if (rightBound) {
					if (y == 1){
						returnArray[CORNERPROTECT]++;
					}
				}
				else{
					int[] attackPostionArray = getAttackPositionValue(Board.get(array, x + 1, y + 1), pieceColor, playerColor);
					returnArray[DANGER] += attackPostionArray[DANGER];
					returnArray[PROTECT] += attackPostionArray[PROTECT];
				}
			}
		} else {
			if (y == 0)
				returnArray[WIN]++; // BLANC GAGNE
			else {
				if (y == 7)
					returnArray[HOMEVALUE]++;
				if (leftBound) {
					if (y == 6){
						returnArray[CORNERPROTECT]++;
					}
				}
				else{
					int[] attackPostionArray = getAttackPositionValue(Board.get(array, x - 1, y - 1), pieceColor, playerColor);
					returnArray[DANGER] += attackPostionArray[DANGER];
					returnArray[PROTECT] += attackPostionArray[PROTECT];
				}
				if (rightBound) {
					if (y == 6){
						returnArray[CORNERPROTECT]++;
					}
				}
				else{
					int[] attackPostionArray = getAttackPositionValue(Board.get(array, x + 1, y - 1), pieceColor, playerColor);
					returnArray[DANGER] += attackPostionArray[DANGER];
					returnArray[PROTECT] += attackPostionArray[PROTECT];
				}
			}
		}

		if (!leftBound && !rightBound && isHLine(Board.get(array, x - 1, y), Board.get(array, x + 1, y), pieceColor)) {
			returnArray[HLINE]++;
		}
		
		return returnArray;
	}

	private static int[] getAttackPositionValue(int attackPositionColor, int pieceColor, int playerColor) {
		
		int[] returnArray = new int[2];

		if (attackPositionColor != Consts.CASE_VIDE) {
			if (attackPositionColor == pieceColor) {
				returnArray[PROTECT]++;
			} else if (pieceColor == playerColor) {
				returnArray[DANGER]++;
			}
		}

		return returnArray;
	}

	// Valeur supplementaire si on a une ligne horizontale de 3+ pieces du meme
	// joueur
	private static boolean isHLine(int HLinePosition1, int HLinePosition2, int pieceColor) {
		return (pieceColor == HLinePosition1 && pieceColor == HLinePosition2);
	}

	// Pour debug
	public static void printBoardAndValue(long[] bitBoard, int value,	int playerColor) {
		
		String player = (playerColor == Consts.CASE_NOIR) ? "Noir (o)" : "Blanc (x)";

		for (int y = 0; y < 8; y++){
			System.out.print((8-y) + " ");
			for (int x = 0; x < 8; x++){
				switch(Board.get(bitBoard, x, y)){
				case Consts.CASE_VIDE:
					System.out.print(". ");
					break;
				case Consts.CASE_NOIR:
					System.out.print("o ");
					break;
				case Consts.CASE_BLANC:
					System.out.print("x ");
					break;
				}
			}
			System.out.println();
		}
		System.out.println("  A B C D E F G H");
		System.out.println("\nJoueur: "+player+"\nValeur du jeu: "+value);
	}
	
	public static void printDetails(int[] valueArray, boolean isComparaison, int total){
		String variation = (isComparaison) ? "Variation " : "";
		String signe = (isComparaison && total>0) ? "+" : "";
		
		System.out.println(variation+"JOUEUR:");
		if (valueArray[Consts.PIECEVALUE_PLAYER] != 0)
		System.out.println("  "+variation+"Nombre de pieces : "+valueArray[Consts.PIECEVALUE_PLAYER]
				+" ("+valueArray[Consts.PIECEVALUE_PLAYER]*Consts.PIECEVALUE+" points).");
		if (valueArray[Consts.HOMEVALUE_PLAYER] != 0)
		System.out.println("  "+variation+"Nombre de pieces sur premiere rangee: "+valueArray[Consts.HOMEVALUE_PLAYER]
				+" ("+valueArray[Consts.HOMEVALUE_PLAYER]*Consts.HOMEVALUE+" points).");
		if (valueArray[Consts.CORNERPROTECT_PLAYER] != 0)
		System.out.println("  "+variation+"Nombre de pieces dans coin 2e rangee: "+valueArray[Consts.CORNERPROTECT_PLAYER]
				+" ("+valueArray[Consts.CORNERPROTECT_PLAYER]*Consts.CORNERPROTECT+" points).");
		if (valueArray[Consts.DANGER_PLAYER] != 0)
		System.out.println("  "+variation+"Nombre de pieces en danger d'attaque: "+valueArray[Consts.DANGER_PLAYER]
				+" ("+valueArray[Consts.DANGER_PLAYER]*Consts.DANGER+" points).");
		if (valueArray[Consts.PROTECT_PLAYER] != 0)
		System.out.println("  "+variation+"Nombre de pieces protegee par piece alliee: "+valueArray[Consts.PROTECT_PLAYER]
				+" ("+valueArray[Consts.PROTECT_PLAYER]*Consts.PROTECT+" points).");
		if (valueArray[Consts.FREE_COLUMN_PLAYER] != 0)
		System.out.println("  "+variation+"Nombre de colonnes libres: "+valueArray[Consts.FREE_COLUMN_PLAYER]
				+" ("+valueArray[Consts.FREE_COLUMN_PLAYER]*Consts.FREE_COLUMN+" points).");
		if (valueArray[Consts.HLINE_PLAYER] != 0)
		System.out.println("  "+variation+"Nombre de pieces en formation horizontale de longueur 3+: "+valueArray[Consts.HLINE_PLAYER]
				+" ("+valueArray[Consts.HLINE_PLAYER]*Consts.HLINE+" points).");
		if (valueArray[Consts.WIN2_PLAYER] != 0)
		System.out.println("  "+variation+"Nombre de pieces gagnantes: "+valueArray[Consts.WIN2_PLAYER]
				+" ("+valueArray[Consts.WIN2_PLAYER]*Consts.WIN2+" points).");
		
		System.out.println("\n"+variation+"ADVERSAIRE:");
		if (valueArray[Consts.PIECEVALUE_OPPONENT] != 0)
		System.out.println("  "+variation+"Nombre de pieces : "+valueArray[Consts.PIECEVALUE_OPPONENT]
				+" ("+valueArray[Consts.PIECEVALUE_OPPONENT]*Consts.PIECEVALUE+" points).");
		if (valueArray[Consts.HOMEVALUE_OPPONENT] != 0)
		System.out.println("  "+variation+"Nombre de pieces sur premiere rangee: "+valueArray[Consts.HOMEVALUE_OPPONENT]
				+" ("+valueArray[Consts.HOMEVALUE_OPPONENT]*Consts.HOMEVALUE+" points).");
		if (valueArray[Consts.CORNERPROTECT_OPPONENT] != 0)
		System.out.println("  "+variation+"Nombre de pieces dans coin 2e rangee: "+valueArray[Consts.CORNERPROTECT_OPPONENT]
				+" ("+valueArray[Consts.CORNERPROTECT_OPPONENT]*Consts.CORNERPROTECT+" points).");
		if (valueArray[Consts.DANGER_OPPONENT] != 0)
		System.out.println("  "+variation+"Nombre de pieces en danger d'attaque: "+valueArray[Consts.DANGER_OPPONENT]
				+" ("+valueArray[Consts.DANGER_OPPONENT]*Consts.DANGER+" points).");
		if (valueArray[Consts.PROTECT_OPPONENT] != 0)
		System.out.println("  "+variation+"Nombre de pieces protegee par piece alliee: "+valueArray[Consts.PROTECT_OPPONENT]
				+" ("+valueArray[Consts.PROTECT_OPPONENT]*Consts.PROTECT+" points).");
		if (valueArray[Consts.FREE_COLUMN_OPPONENT] != 0)
		System.out.println("  "+variation+"Nombre de colonnes libres: "+valueArray[Consts.FREE_COLUMN_OPPONENT]
				+" ("+valueArray[Consts.FREE_COLUMN_OPPONENT]*Consts.FREE_COLUMN+" points).");
		if (valueArray[Consts.HLINE_OPPONENT] != 0)
		System.out.println("  "+variation+"Nombre de pieces en formation horizontale de longueur 3+: "+valueArray[Consts.HLINE_OPPONENT]
				+" ("+valueArray[Consts.HLINE_OPPONENT]*Consts.HLINE+" points).");
		if (valueArray[Consts.WIN2_OPPONENT] != 0)
		System.out.println("  "+variation+"Nombre de pieces gagnantes: "+valueArray[Consts.WIN2_PLAYER]
				+" ("+valueArray[Consts.WIN2_OPPONENT]*Consts.WIN2+" points).");
		
		System.out.println("\n"+variation+"TOTAL: "+signe+total+" points\n");
	}
	
	public static void printDifferences (int[] valueArray1, int[] valueArray2, int total1, int total2){
		for (int i=0; i<valueArray2.length; i++){
			valueArray2[i] -= valueArray1[i];
		}
		total2 -= total1;
		printDetails(valueArray2, true, total2);
	}
}
